# 维龙货位盘点

import json
import os
import sys
import threading
import time
import tkinter as tk
from tkinter import ttk, messagebox

import keyboard
import pyautogui


class AutoClickerGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("自动点击器")
        self.root.geometry("800x600")

        self.stop_event = threading.Event()
        self.config = self.load_config()
        self.click_positions = []  # 存储点击位置列表

        self.create_widgets()
        self.load_values_to_gui()

    def create_widgets(self):
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # 基本参数框架
        params_frame = ttk.LabelFrame(main_frame, text="基本参数", padding="5")
        params_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5)

        # 间隔时间
        ttk.Label(params_frame, text="间隔时间(毫秒):").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.interval_entry = ttk.Entry(params_frame)
        self.interval_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5, padx=5)

        # 重复次数
        ttk.Label(params_frame, text="重复次数:").grid(row=0, column=2, sticky=tk.W, pady=5)
        self.repeat_entry = ttk.Entry(params_frame)
        self.repeat_entry.grid(row=0, column=3, sticky=(tk.W, tk.E), pady=5, padx=5)

        # 点击序列编辑区域
        clicks_frame = ttk.LabelFrame(main_frame, text="点击序列编辑", padding="5")
        clicks_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)

        # 创建树形视图来显示点击序列
        self.tree = ttk.Treeview(clicks_frame, columns=('序号', 'X坐标', 'Y坐标', '等待时间'), show='headings')
        self.tree.heading('序号', text='序号', anchor='center')
        self.tree.heading('X坐标', text='X坐标', anchor='center')
        self.tree.heading('Y坐标', text='Y坐标', anchor='center')
        self.tree.heading('等待时间', text='等待时间(ms)', anchor='center')

        # 设置每列的文字居中
        self.tree.column('序号', anchor='center', width=100)
        self.tree.column('X坐标', anchor='center', width=100)
        self.tree.column('Y坐标', anchor='center', width=100)
        self.tree.column('等待时间', anchor='center', width=100)

        self.tree.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S))

        # 添加滚动条
        scrollbar = ttk.Scrollbar(clicks_frame, orient=tk.VERTICAL, command=self.tree.yview)
        scrollbar.grid(row=0, column=2, sticky=(tk.N, tk.S))
        self.tree.configure(yscrollcommand=scrollbar.set)

        # 编辑区域
        edit_frame = ttk.Frame(clicks_frame)
        edit_frame.grid(row=1, column=0, columnspan=2, pady=5)

        ttk.Label(edit_frame, text="X:").grid(row=0, column=0)
        self.x_entry = ttk.Entry(edit_frame, width=10)
        self.x_entry.grid(row=0, column=1, padx=2)

        ttk.Label(edit_frame, text="Y:").grid(row=0, column=2)
        self.y_entry = ttk.Entry(edit_frame, width=10)
        self.y_entry.grid(row=0, column=3, padx=2)

        ttk.Label(edit_frame, text="等待时间:").grid(row=0, column=4)
        self.wait_entry = ttk.Entry(edit_frame, width=10)
        self.wait_entry.grid(row=0, column=5, padx=2)

        # 编辑按钮
        btn_frame = ttk.Frame(clicks_frame)
        btn_frame.grid(row=2, column=0, columnspan=2, pady=5)

        ttk.Button(btn_frame, text="添加位置", command=self.add_position).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame, text="修改选中", command=self.edit_position).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame, text="删除选中", command=self.delete_position).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame, text="获取当前位置", command=self.get_cursor_position).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame, text="保存配置", command=self.save_config).pack(side=tk.LEFT, padx=2)

        # 控制按钮
        control_frame = ttk.Frame(main_frame)
        control_frame.grid(row=2, column=0, columnspan=2, pady=10)

        self.start_button = ttk.Button(control_frame, text="开始", command=self.start_program)
        self.start_button.pack(side=tk.LEFT, padx=5)

        self.stop_button = ttk.Button(control_frame, text="停止", command=self.stop_program)
        self.stop_button.pack(side=tk.LEFT, padx=5)
        self.stop_button['state'] = 'disabled'

    def load_config(self):

        try:
            base_path = os.path.dirname(os.path.realpath(sys.argv[0]))
            config_path = os.path.join(base_path, 'config.json')
            with open(config_path, 'r') as f:
                return json.load(f)
        except Exception as e:
            messagebox.showerror("错误", f"加载配置文件失败: {str(e)}")
            return None

    def load_values_to_gui(self):
        if self.config:
            self.interval_entry.insert(0, str(self.config['interval_ms']))
            self.repeat_entry.insert(0, str(self.config['repeat_times']))

            # 加载点击序列到树形视图
            for i, click in enumerate(self.config['click_sequence'], 1):
                pos = click['position']
                self.tree.insert('', 'end', values=(i, pos[0], pos[1], click['wait_time']))

    def add_position(self):
        try:
            x = int(self.x_entry.get())
            y = int(self.y_entry.get())
            wait = int(self.wait_entry.get())

            # 获取当前序列长度
            next_index = len(self.tree.get_children()) + 1

            # 插入新位置
            self.tree.insert('', 'end', values=(next_index, x, y, wait))

            # 清空输入框
            self.x_entry.delete(0, tk.END)
            self.y_entry.delete(0, tk.END)
            self.wait_entry.delete(0, tk.END)

        except ValueError:
            messagebox.showerror("错误", "请输入有效的数字")

    def edit_position(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择一个点击位置")
            return

        try:
            x = int(self.x_entry.get())
            y = int(self.y_entry.get())
            wait = int(self.wait_entry.get())

            # 更新选中项
            item = selected[0]
            current_index = self.tree.item(item)['values'][0]
            self.tree.item(item, values=(current_index, x, y, wait))

        except ValueError:
            messagebox.showerror("错误", "请输入有效的数字")

    def delete_position(self):
        selected = self.tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择一个点击位置")
            return

        self.tree.delete(selected)
        # 重新编号
        for i, item in enumerate(self.tree.get_children(), 1):
            values = self.tree.item(item)['values']
            self.tree.item(item, values=(i,) + tuple(values[1:]))

    def get_cursor_position(self):
        self.root.iconify()  # 最小化窗口

        # 创建一个临时窗口来捕获鼠标事件
        temp_window = tk.Toplevel()
        temp_window.attributes('-alpha', 0.01)  # 设置几乎透明
        temp_window.attributes('-fullscreen', True)
        temp_window.attributes('-topmost', True)

        # 用于跟踪双击
        last_click_time = [0]

        def on_right_click(event):
            current_time = time.time()
            if current_time - last_click_time[0] < 0.3:  # 300ms内的双击
                pos = (event.x_root, event.y_root)
                self.x_entry.delete(0, tk.END)
                self.y_entry.delete(0, tk.END)
                self.x_entry.insert(0, str(pos[0]))
                self.y_entry.insert(0, str(pos[1]))
                temp_window.destroy()
                self.root.deiconify()  # 恢复主窗口
            last_click_time[0] = current_time

        temp_window.bind('<Button-3>', on_right_click)  # 绑定鼠标右键事件

        # 显示提示标签
        label = tk.Label(temp_window,
                         text="双击鼠标右键获取位置\nESC键取消",
                         font=('Arial', 24),
                         bg='white')
        label.pack(expand=True)

        def on_escape(event):
            temp_window.destroy()
            self.root.deiconify()

        temp_window.bind('<Escape>', on_escape)

    def save_config(self):
        try:
            # 从界面获取当前配置
            config = {
                'interval_ms': int(self.interval_entry.get()),
                'repeat_times': int(self.repeat_entry.get()),
                'click_sequence': []
            }

            # 获取所有点击位置
            for item in self.tree.get_children():
                values = self.tree.item(item)['values']
                config['click_sequence'].append({
                    'position': [values[1], values[2]],
                    'wait_time': values[3]
                })

            # 保存到文件
            base_path = os.path.dirname(os.path.realpath(sys.argv[0]))
            config_path = os.path.join(base_path, 'config.json')
            with open(config_path, 'w') as f:
                json.dump(config, f, indent=4)

            messagebox.showinfo("成功", "配置已保存")

        except Exception as e:
            messagebox.showerror("错误", f"保存配置失败: {str(e)}")

    def start_program(self):
        try:
            # 从界面获取最新配置
            self.config = {
                'interval_ms': int(self.interval_entry.get()),
                'repeat_times': int(self.repeat_entry.get()),
                'click_sequence': []
            }

            # 获取所有点击位置
            for item in self.tree.get_children():
                values = self.tree.item(item)['values']
                self.config['click_sequence'].append({
                    'position': [int(values[1]), int(values[2])],
                    'wait_time': int(values[3])
                })

            if not self.config['click_sequence']:
                messagebox.showwarning("警告", "请先添加点击位置")
                return

            self.stop_event.clear()
            self.start_button['state'] = 'disabled'
            self.stop_button['state'] = 'normal'

            # 在新线程中运行自动点击
            self.click_thread = threading.Thread(
                target=auto_click_sequence,
                args=(self.stop_event, self.config),
                daemon=True
            )
            self.click_thread.start()

            # 启动监听键盘的线程
            self.keyboard_thread = threading.Thread(
                target=self.listen_for_ctrl_z,
                daemon=True
            )
            self.keyboard_thread.start()

        except ValueError as e:
            messagebox.showerror("错误", "请输入有效的数字")
            self.stop_program()

    def stop_program(self):
        self.stop_event.set()
        self.start_button['state'] = 'normal'
        self.stop_button['state'] = 'disabled'

    def listen_for_ctrl_z(self):
        while not self.stop_event.is_set():
            if keyboard.is_pressed('ctrl+z'):
                print("检测到Ctrl+Z，程序正在退出...")
                self.stop_program()
                break
            time.sleep(0.1)


def ms_to_seconds(ms):
    return ms / 1000.0


def auto_click_sequence(stop_event, config):
    # 保持原有的自动点击函数逻辑
    click_sequence = config['click_sequence']
    interval_ms = config['interval_ms']
    repeat_times = config['repeat_times']

    try:
        print("当前使用的配置:")
        for i, click in enumerate(click_sequence, 1):
            print(f"第{i}个点击位置: {click['position']}, 等待时间: {click['wait_time']}毫秒")
        print(f"下一次循环等待时间: {interval_ms}毫秒")
        print(f"重复次数: {repeat_times}次\n")

        print(f"程序将在3000毫秒后开始执行...")
        time.sleep(3)

        for i in range(repeat_times):
            if stop_event.is_set():
                print("检测到停止信号，退出自动点击。")
                break

            print(f"\n开始执行第 {i + 1}/{repeat_times} 次序列")

            for j, click in enumerate(click_sequence, 1):
                if stop_event.is_set():
                    break

                pos = click['position']
                wait = click['wait_time']
                pyautogui.moveTo(pos[0], pos[1], duration=0.5)
                pyautogui.click()

                if j < len(click_sequence):
                    time.sleep(ms_to_seconds(wait))

            if i < repeat_times - 1 and not stop_event.is_set():
                print(f"\n等待{interval_ms}毫秒后开始下一次循环...")
                time.sleep(ms_to_seconds(interval_ms))

        if not stop_event.is_set():
            print("\n所有点击序列执行完成！")

    except pyautogui.FailSafeException:
        print("触发了故障保护，程序已停止")


if __name__ == "__main__":
    root = tk.Tk()
    app = AutoClickerGUI(root)
    root.mainloop()